package net.oschina.arvin.sqlbuilder;

import net.oschina.arvin.sqlbuilder.definition.ColumnDefinition;
import net.oschina.arvin.sqlbuilder.definition.ConditionItem;
import net.oschina.arvin.sqlbuilder.enums.CompareType;
import net.oschina.arvin.sqlbuilder.utils.AssertUtil;
import net.oschina.arvin.sqlbuilder.utils.Util;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author Arvin
 * @time 2017/11/17 20:11
 */
public abstract class AbstractSelectBuilder<T> extends AbstractQueryBuilder<T> {

    /** 分页查询的时候，页码 */
    private int pageNo;
    /** 分页查询的时候，每页查询多少记录 */
    private int pageSize;

    /** Order By 子句 sql */
    private String orderBySql;

    /** limit语句sql */
    private String limitSql;

    /** 要查询的列语句 */
    private String selectColumnSql;

    /** Where 条件构造器 */
    private final WhereBuilder whereBuilder;

    /** 自定义要查询的列 */
    private List<ColumnDefinition> customSelectColumnDefinitions = new ArrayList<ColumnDefinition>();

    /** 自定义要忽略查询的列 */
    private List<ColumnDefinition> customIgnoreSelectColumnDefinitions = new ArrayList<ColumnDefinition>();

    /** 查询条件项 */
    private List<ConditionItem> conditionItemList = new ArrayList<ConditionItem>();

    public AbstractSelectBuilder(Class<?> modelType) {
        super(modelType, SqlBuilderType.SELECT);

        this.whereBuilder = new WhereBuilder(modelType);
    }

    /**
     * 设置分页参数
     *
     * @param pageNo   要查询的页码
     * @param pageSize 每页显示多少条
     */
    public T limit(int pageNo, int pageSize) {
        AssertUtil.assertTrue(pageNo > 0 && pageSize > 0, "分页参数不正确，要求 pageNo > 0 并且 pageSize > 0");
        this.pageNo = pageNo;
        this.pageSize = pageSize;
        return self();
    }

    /**
     * 只查询一个
     */
    public T limitOne() {
        return limit(1, 1);
    }

    public int getPageNo() {
        return pageNo;
    }

    public int getPageSize() {
        return pageSize;
    }


    public T addCustomSelectModelFieldNames(String... modelFieldNames) {
        if (null != modelFieldNames && modelFieldNames.length > 0) {
            for (String modelFieldName : modelFieldNames) {
                ColumnDefinition columnDefinition = getColumnDefinitionByModelFieldName(modelFieldName);
                if (null != columnDefinition) {
                    customSelectColumnDefinitions.add(columnDefinition);
                }
            }
        }
        return self();
    }


    public T addIgnoreCustomSelectModelFieldNames(String... modelFieldNames) {
        if (null != modelFieldNames && modelFieldNames.length > 0) {
            for (String modelFieldName : modelFieldNames) {
                ColumnDefinition columnDefinition = getColumnDefinitionByModelFieldName(modelFieldName);
                if (null != columnDefinition) {
                    customIgnoreSelectColumnDefinitions.add(columnDefinition);
                }
            }
        }
        return self();
    }

    /** 自定义要忽略的模型查询条件 */
    private Set<Field> customIgnoreModelQueryConditionFieldSet = new HashSet<Field>();

    /**
     * 添加自定义要忽略的查询条件
     *
     * @param modelFieldNames java字段属性名称列表
     */
    public T addIgnoreConditionField(String... modelFieldNames) {
        if (null != modelFieldNames && modelFieldNames.length > 0) {
            for (String modelFieldName : modelFieldNames) {
                ColumnDefinition columnDefinition = getColumnDefinitionByModelFieldName(modelFieldName);
                if (null != columnDefinition) {
                    customIgnoreModelQueryConditionFieldSet.add(columnDefinition.getField());
                }
            }
        }
        return self();
    }

    /**
     * 是否要忽略这个模型属性的查询条件
     *
     * @param modelColumnDefinition 模型列定义
     * @return
     */
    protected boolean isCustomIgnoreCondition(ColumnDefinition modelColumnDefinition) {
        return customIgnoreModelQueryConditionFieldSet.contains(modelColumnDefinition.getField());
    }

    @Override
    protected String build() {
        // 构造 Where 语句
        buildWhereClause();

        // 构造 OrderBySql 排序属性
        buildOrderBySql();

        // 构造 LIMIT 子句
        buildLimitSql();

        // 构造要查询的列语句
        buildSelectColumnSql();

        // 构造不包含的 ORDER BY 和 LIMIT 子句的 FROM WHERE 语句
        StringBuilder sqlBuilder = buildFromWhereSql();

        return sqlBuilder.toString();
    }

    /**
     * 构造Where子句
     */
    private void buildWhereClause() {
        List<ConditionItem> conditionItemList = parseConditionItemList();
        if (Util.isNotEmpty(conditionItemList)) {
            for (ConditionItem conditionItem : conditionItemList) {
                conditionItem.appendToWhereBuilder(getQueryCondition(), whereBuilder,
                        getCustomCompareType(conditionItem.getQueryConditionField()));
            }
        }
    }

    protected abstract Object getQueryCondition();

    protected abstract List<ConditionItem> parseConditionItemList();

    private StringBuilder buildFromWhereSql() {
        StringBuilder sqlBuilder = new StringBuilder(" FROM ").append(getWrapTablename());
        if (whereBuilder != null && whereBuilder.conditionCount() > 0) {
            sqlBuilder.append(whereBuilder.getSql());
            addListSqlParams(whereBuilder.getSqlParamList());
        }
        return sqlBuilder;
    }

    /**
     * 构建 select 的列语句
     */
    private String buildSelectColumnSql() {

        this.selectColumnSql = null;

        boolean hasCustomIgnoreSelectColumn = Util.isNotEmpty(this.customIgnoreSelectColumnDefinitions);
        boolean hasCustomSelectColumn = Util.isNotEmpty(this.customSelectColumnDefinitions);

        if (!hasCustomIgnoreSelectColumn && !hasCustomSelectColumn) {
            this.selectColumnSql = "*";
            return this.selectColumnSql;
        }

        Set<ColumnDefinition> tempColumnDefinitions = new HashSet<ColumnDefinition>();
        if (hasCustomIgnoreSelectColumn && !hasCustomSelectColumn) {
            tempColumnDefinitions.addAll(getModelDefinition().getColumnDefinitionList());
        } else {
            tempColumnDefinitions.addAll(this.customSelectColumnDefinitions);
        }

        if (Util.isNotEmpty(tempColumnDefinitions)) {
            StringBuilder builder = new StringBuilder();
            for (ColumnDefinition columnDefinition : tempColumnDefinitions) {
                if (!this.customIgnoreSelectColumnDefinitions.contains(columnDefinition)) {
                    builder.append(columnDefinition.getWrapColumnName()).append(",");
                }
            }
            if (builder.length() > 0) {
                builder.setLength(builder.length() - 1);
                this.selectColumnSql = builder.toString();
            } else {
                this.selectColumnSql = "*";
            }
            return this.selectColumnSql;
        } else {
            this.selectColumnSql = "*";
        }
        return this.selectColumnSql;
    }

    private void buildLimitSql() {
        this.limitSql = "";
        if (Util.isValidPagingParams(pageNo, pageSize)) {
            if (pageSize == 1) {
                this.limitSql = " LIMIT " + pageSize;
            } else {
                this.limitSql = " LIMIT " + pageNo + "," + pageSize;
            }
        }
    }

    /**
     * 构造 OrderBy 语句
     */
    private void buildOrderBySql() {
        this.orderBySql = "";
        List<OrderItem> orderItemList = parseOrderItemList();
        if (Util.isNotEmpty(orderItemList)) {
            StringBuilder orderBySqlBuilder = new StringBuilder(" ORDER BY ");
            for (OrderItem orderItem : orderItemList) {
                String columnName = orderItem.getWrapColumnName();
                orderBySqlBuilder.append(columnName).append(" ")
                        .append(orderItem.isAsc() ? ORDER_BY_ASC : ORDER_BY_DESC).append(",");
            }
            orderBySqlBuilder.setLength(orderBySqlBuilder.length() - 1);
            this.orderBySql = orderBySqlBuilder.toString();
        }
    }

    /** 自定义的字段比较类型 */
    private Map<Field, CompareType> customCompareTypeMap = new HashMap<Field, CompareType>();

    public CompareType getCustomCompareType(Field queryConditionField) {
        AssertUtil.assertNotNull(queryConditionField, "要获取自定义比较类型的字段不存在！");
        return customCompareTypeMap.get(queryConditionField);
    }

    public T setCompareType(String modelFieldName, CompareType compareType) {
        AssertUtil.assertNotNull(modelFieldName, "要添加自定义查询比较类型的java属性名称不能为空！");
        ColumnDefinition columnDefinition = getColumnDefinitionByModelFieldName(modelFieldName);
        AssertUtil.assertNotNull(columnDefinition, "要添加自定义查询比较类型的java属性名称不存在！");
        customCompareTypeMap.put(columnDefinition.getField(), compareType);
        return self();
    }

    /**
     * 获取查询SQL
     *
     * @return
     */
    public String getSelectSql() {
        String fromWhereSql = getSql();
        return "SELECT " + this.selectColumnSql + fromWhereSql + this.orderBySql + this.limitSql;
    }

    /**
     * 获取查询数量的SQL
     *
     * @return
     */
    public String getCountSql() {
        return "SELECT COUNT(*)" + this.getSql();
    }

    /**
     * 解析排序字段列表
     *
     * @return 返回需要排序的字段列表
     */
    protected abstract List<OrderItem> parseOrderItemList();
}
